﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MosCraft.Utilities;

namespace MosCraft.Terrain.MapGen
{
    class MapGenCaves : MapGenBase
    {
        public int amount = 40;
        public int rarity = 15;
        public int bigRarity = 4;
        public int branches = 4;

        public MapGenCaves()
        {
        }
       
        protected void generateLargeCaveNode(int x, int z, ref byte[] blocks, double d, double d1, double d2)
        {
            generateCaveNode(x, z, ref blocks, d, d1, d2, 1.0F + this.rand.nextFloat() * 6.0F, 0.0F, 0.0F, -1, -1, 0.5D);
        }

        protected void generateCaveNode(int x, int z, ref byte[] blocks, double d, double d1, double d2, float f, float f1, float f2, int k, int l, double d3)
        {
            double d4 = x * 16 + 8;
            double d5 = z * 16 + 8;
            float f3 = 0.0F;
            float f4 = 0.0F;

            FastRandom random = new FastRandom(rand.nextLong());

            if (l <= 0)
            {
                int i1 = this.range * 16 - 16;
                l = i1 - random.nextInt(i1 / 4);
            }

            bool flag = false;

            if (k == -1)
            {
                k = l / 2;
                flag = true;
            }

            int j1 = rand.nextInt(l / 2) + l / 4;
            bool flag1 = rand.nextInt(6) == 0;

            for (; k < l; k++)
            {
                double d6 = 1.5D + Math.Sin(k * 3.141593F / l) * f * 1.0F;
                double d7 = d6 * d3;
                float f5 = QuickMath.Cos(f2);
                float f6 = QuickMath.Sin(f2);
                d += QuickMath.Cos(f1) * f5;
                d1 += f6;
                d2 += QuickMath.Sin(f1) * f5;

                if (flag1)
                {
                    f2 *= 0.92F;
                }
                else
                {
                    f2 *= 0.7F;
                }

                f2 += f4 * 0.1F;
                f1 += f3 * 0.1F;
                f4 *= 0.9F;
                f3 *= 0.75F;

                f4 += (random.nextFloat() - random.nextFloat()) * random.nextFloat() * 2.0F;
                f3 += (random.nextFloat() - random.nextFloat()) * random.nextFloat() * 4.0F;

                if ((!flag) && (k == j1) && (f > 1.0F))
                {
                    this.generateCaveNode(x, z, ref blocks, d, d1, d2, rand.nextFloat() * 0.5F + 0.5F, f1 - 1.570796F, f2 / 3.0F, k, l, 1.0D);
                    this.generateCaveNode(x, z, ref blocks, d, d1, d2, rand.nextFloat() * 0.5F + 0.5F, f1 + 1.570796F, f2 / 3.0F, k, l, 1.0D);
                    return;
                }

                if ((!flag) && (rand.nextInt(4) == 0))
                    continue;

                double d8 = d - d4;
                double d9 = d2 - d5;
                double d10 = l - k;
                double d11 = f + 2.0F + 16.0F;

                if (d8 * d8 + d9 * d9 - d10 * d10 > d11 * d11)
                    return;

                if ((d < d4 - 16.0D - d6 * 2.0D) || (d2 < d5 - 16.0D - d6 * 2.0D) || (d > d4 + 16.0D + d6 * 2.0D) || (d2 > d5 + 16.0D + d6 * 2.0D))
                    continue;

                d8 = Math.Floor(d - d6) - x * 16 - 1;
                int k1 = (int)Math.Floor(d + d6) - x * 16 + 1;
                d9 = Math.Floor(d1 - d7) - 1;
                int l1 = (int)Math.Floor(d1 + d7) + 1;
                d10 = Math.Floor(d2 - d6) - z * 16 - 1;
                int i2 = (int)Math.Floor(d2 + d6) - z * 16 + 1;

                if (d8 < 0.0D)
                    d8 = 0.0D;

                if (k1 > 16)
                    k1 = 16;

                if (d9 < 1.0D)
                    d9 = 1.0D;

                if (l1 > 120)
                    l1 = 120;

                if (d10 < 0.0D)
                    d10 = 0.0D;

                if (i2 > 16)
                    i2 = 16;

                bool flag2 = false;

                for (int j2 = (int)d8; (!flag2) && (j2 < k1); j2++)
                {
                    for (int l2 = (int)d10; (!flag2) && (l2 < i2); l2++)
                    {
                        for (int i3 = l1 + 1; (!flag2) && (i3 >= d9 - 1.0D); i3--)
                        {
                            int j3 = (j2 * 16 + l2) * 128 + i3;
                            if ((i3 < 0) || (i3 >= 128))
                            {
                                continue;
                            }
                            //if ((blocks[j3] == yy.A.bM) || (blocks[j3] == yy.B.bM))
                            //{
                            //    flag2 = true;
                            //}
                            if ((i3 == d9 - 1.0D) || (j2 == d8) || (j2 == k1 - 1) || (l2 == d10) || (l2 == i2 - 1))
                                continue;
                            i3 = (int)d9;
                        }

                    }

                }

                if (flag2)
                    continue;

                for (int k2 = (int)d8; k2 < k1; k2++)
                {
                    double d12 = (k2 + x * 16 + 0.5D - d) / d6;
                    for (int k3 = (int)d10; k3 < i2; k3++)
                    {
                        double d13 = (k3 + z * 16 + 0.5D - d2) / d6;
                        int blockNum = (k2 * 16 + k3) * 128 + l1;
                        bool flag3 = false;

                        for (int i4 = l1 - 1; i4 >= d9; i4--)
                        {
                            double d14 = (i4 + 0.5D - d1) / d7;
                            if ((d14 > -0.7D) && (d12 * d12 + d14 * d14 + d13 * d13 < 1.0D))
                            {
                                byte byte0 = blocks[blockNum];

                                if (byte0 == Block.Grass.blockID)
                                {
                                    flag3 = true;
                                }

                                if ((byte0 == Block.Stone.blockID) || (byte0 == Block.Dirt.blockID) || (byte0 == Block.Grass.blockID))
                                {
                                    if (i4 < 10)
                                    {
                                        //blocks[l3] = (byte)yy.D.bM;
                                    }
                                    else
                                    {
                                        blocks[blockNum] = 0; //set to air

                                        if ((flag3) && (blocks[(blockNum - 1)] == Block.Dirt.blockID))
                                        {
                                            blocks[(blockNum - 1)] = (byte)Block.Grass.blockID;
                                        }
                                    }
                                }
                            }
                            blockNum--;
                        }

                    }

                }

                if (flag)
                    break;
            }
        }

        protected override void recursiveGenerate(World world, int xRange, int zRange, int x, int z, ref byte[] blocks)
        {
            int caves = rand.nextInt(rand.nextInt(rand.nextInt(amount) + 1) + 1);

            if (rand.nextInt(rarity) != 0)
            {
                caves = 0;
            }

            for (int j1 = 0; j1 < caves; j1++)
            {
                double d = xRange * 16 + rand.nextInt(16);
                double d1 = rand.nextInt(rand.nextInt(world.getMaxHeight() - 8) + 8);
                double d2 = zRange * 16 + rand.nextInt(16);
                int k1 = 1;

                if (rand.nextInt(bigRarity) == 0)
                {
            	    generateLargeCaveNode(x, z, ref blocks, d, d1, d2);
                    k1 += rand.nextInt(branches);
                }

                for (int l1 = 0; l1 < k1; l1++)
                {
                    float f = rand.nextFloat() * 3.141593F * 2.0F;
                    float f1 = ((rand.nextFloat() - 0.5F) * 2.0F) / 8F;
                    float f2 = rand.nextFloat() * 2.0F + rand.nextFloat();

                    if (rand.nextInt(10) == 0)
                    {
                        f2 *= rand.nextFloat() * rand.nextFloat() * 3F + 1.0F;
                    }

                    generateCaveNode(x, z, ref blocks, d, d1, d2, f2, f, f1, 0, 0, 1.0D);
                }
            }
        }
    }
}
